package com.caine.mybatis.scan;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * 类路径扫描，通过指定的路径获取类文件
 * @author jiangxian
 *
 */
@Slf4j
public class ClassPathScanComponent implements ResourceLoaderAware{
	
	
	private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
	
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);

	private String resourcePattern = DEFAULT_RESOURCE_PATTERN;

	/**
	 * 包括的过滤条件
	 */
	private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();

	/**
	 * 需要排除的过滤条件
	 */
	private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
	
	

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
	}

	/**
	 * 扫描包下匹配的类
	 * @param basePackages
	 * @return
	 */
	public Set<MetadataReader> scan(String... basePackages) throws Exception {
		Set<MetadataReader> components = new LinkedHashSet<MetadataReader>();
		for(String basePackage : basePackages){
			Set<MetadataReader> candidates = findCandidateComponents(basePackage);
			components.addAll(candidates);
		}
		return components;
	}
	
	private Set<MetadataReader> findCandidateComponents(String basePackage) throws Exception {
		if(log.isDebugEnabled()){
			log.debug("scan component from " + basePackage);
		}
		
		Set<MetadataReader> candidates = new LinkedHashSet<MetadataReader>();
		try{
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + 
					resolveBasePackage(basePackage) + "/" + this.resourcePattern;
			//获取所有扫描到的资源
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			
			boolean traceEnabled = log.isTraceEnabled();
			boolean debugEnabled = log.isDebugEnabled();
			
			for(Resource resource : resources){
				if(traceEnabled){
					log.trace("Scanning " + resource);
				}
				if(resource.isReadable()){
					//元数据读取器获取类的元数据信息
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
					//判断是否要读取的获取对象
					if(isCandidateComponent(metadataReader)){
						if(debugEnabled){
							log.debug("找到一个匹配的类："+resource);
						}
						candidates.add(metadataReader);
					}else{
						if(debugEnabled){
							log.debug("不匹配的类：" + resource);
						}
					}
				}else{
					if(traceEnabled){
						log.trace("此类不可读："+ resource);
					}
				}
			}
		}catch(IOException ex){
			throw new Exception("在扫描classpath是IO读取失败",ex);
		}
		return candidates;
	}

	/**
	 * 是否匹配过滤条件
	 * @param metadataReader
	 * @return
	 * @throws IOException 
	 */
	private boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		//先筛选不包括的匹配条件
		for(TypeFilter tf : this.excludeFilters){
			if(tf.match(metadataReader, this.metadataReaderFactory)){
				return false;
			}
		}
		//必须匹配的过滤条件
		for(TypeFilter tf : this.includeFilters){
			if(tf.match(metadataReader, metadataReaderFactory)){
				return true;
			}
		}
		return false;
	}

	/**
	 * 解析用户设置的包路径，将.转换成/
	 * @param basePackage
	 * @return
	 */
	private String resolveBasePackage(String basePackage) {
		return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
	}

	/**
	 * 用于匹配扫描的文件
	 * @param resourcePattern
	 */
	public void setResourcePattern(String resourcePattern){
		this.resourcePattern = resourcePattern;
	}
	/**
	 * 查找时需要匹配的过滤条件
	 * @param annotationKlasses
	 */
	@SuppressWarnings("unchecked")
	public void setIncludeAnnotations(Class... annotationKlasses){
		for(Class annotationClass : annotationKlasses){
			this.includeFilters.add(new AnnotationTypeFilter(annotationClass));
		}
	}
	/**
	 * 不能包含的过滤条件
	 * @param annotationKlasses
	 */
	@SuppressWarnings("unchecked")
	public void setExcludAnnotations(Class... annotationKlasses){
		for(Class annotationClass : annotationKlasses){
			this.excludeFilters.add(0,new AnnotationTypeFilter(annotationClass));
		}
	}
	
	public void resetFilters(){
		this.includeFilters.clear();
		this.excludeFilters.clear();
	}
}
